package net.j4love.netty.inaction.c4;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.oio.OioServerSocketChannel;
import io.netty.util.CharsetUtil;
import org.junit.Test;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Objects;

/**
 * @author he peng
 * @create 2018/6/12 14:48
 * @see
 */
public class TransportMigrateTest {

    // 传输迁移案列

    static final InetSocketAddress SERVER_ADDRESS = new InetSocketAddress("127.0.0.1" , 9999);

    @Test
    public void client() throws Exception {
        Socket socket = new Socket();
        socket.connect(SERVER_ADDRESS);
        BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
        byte[] buf = new byte[1024];
        int readSize;
        StringBuilder builder = new StringBuilder();
        while ((readSize = in.read(buf)) != -1) {
            builder.append(new String(buf , 0 , readSize , Charset.forName("utf-8")));
        }
        System.out.println("[" + Thread.currentThread().getName() + "] Thread ," +
                " Client Received Msg : " + builder.toString());

        in.close();
        socket.close();
    }

    @Test
    public void plainOioServer() {

        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket();
            serverSocket.bind(SERVER_ADDRESS);
            System.out.println("********************* [" + Thread.currentThread().getName() + "] Thread , " +
                    "OIO server start in " + SERVER_ADDRESS.getPort() + " port *********************");
            while (true) {
                final Socket socket = serverSocket.accept();
                if (Objects.isNull(socket)) {
                    continue;
                }
                System.out.println("[" + Thread.currentThread().getName() + "] Thread , Accepted Connection from + " + socket.getRemoteSocketAddress());
                new Thread(() -> {
                    OutputStream out = null;
                    try {
                        out = socket.getOutputStream();
                        String msg = "Hi!";
                        out.write((msg + "\r\n").getBytes(Charset.forName("utf-8")));
                        out.flush();
                        System.out.println("[" + Thread.currentThread().getName() + "] Thread , write msg -> " + msg);
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (Objects.nonNull(out)) {
                            try {
                                out.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                } , "PlainOioServerHandleThread").start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (Objects.nonNull(serverSocket)) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    @Test
    public void plainNioServer() {
        Selector selector = null;
        ServerSocketChannel serverSocketChannel = null;
        try {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(SERVER_ADDRESS);
            System.out.println("********************* [" + Thread.currentThread().getName() + "] Thread , " +
                    "NIO server start in " + SERVER_ADDRESS.getPort() + " port *********************");
            serverSocketChannel.register(selector , SelectionKey.OP_ACCEPT);
            while (true) {
                int selectedNum = selector.select();
                if (selectedNum == 0) {
                    continue;
                }
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey sk = iterator.next();
                    iterator.remove();
                    if (! sk.isValid()) {
                        continue;
                    } else if (sk.isAcceptable()) {
                        ServerSocketChannel ssChannel = (ServerSocketChannel) sk.channel();
                        SocketChannel socketChannel = ssChannel.accept();
                        if (Objects.isNull(socketChannel)) {
                            continue;
                        }
                        System.out.println("[" + Thread.currentThread().getName() + "] Thread , Accepted Connection from + " + socketChannel.getRemoteAddress());
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector , SelectionKey.OP_WRITE | SelectionKey.OP_READ);
                    } else if (sk.isConnectable()) {

                    } else if (sk.isReadable()) {

                    } else if (sk.isWritable()) {
                        SocketChannel channel = (SocketChannel) sk.channel();
                        String msg = "Hi!";
                        byte[] bytes = (msg + "\r\n").getBytes(Charset.forName("utf-8"));
                        ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
                        byteBuffer.put(bytes);
                        byteBuffer.flip();
                        channel.write(byteBuffer);
                        System.out.println("[" + Thread.currentThread().getName() + "] Thread , write msg -> " + msg);
                        channel.close();
                    } else {

                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (Objects.nonNull(selector)) {
                try {
                    selector.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (Objects.nonNull(serverSocketChannel)) {
                try {
                    serverSocketChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    public void nettyOioServer() {
        OioEventLoopGroup group = new OioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(group)
                        .channel(OioServerSocketChannel.class)
                        .localAddress(SERVER_ADDRESS)
                        .childHandler(new ChannelInitializer<io.netty.channel.socket.SocketChannel>() {
                            @Override
                            protected void initChannel(io.netty.channel.socket.SocketChannel ch) throws Exception {
                                System.out.println("[" + Thread.currentThread().getName() + "] Thread , Accepted Connection from + " + ch.remoteAddress());
                                ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                                    @Override
                                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                        String msg = "Hi!";
                                        ctx.writeAndFlush(Unpooled.copiedBuffer(msg , CharsetUtil.UTF_8))
                                            .addListener(ChannelFutureListener.CLOSE);
                                        System.out.println("[" + Thread.currentThread().getName() + "] Thread , write msg -> " + msg);
                                    }
                                });
                            }
                        });
            ChannelFuture future = serverBootstrap.bind().sync();
            System.out.println("********************* [" + Thread.currentThread().getName() + "] Thread , " +
                    "Netty OIO server start in " + SERVER_ADDRESS.getPort() + " port *********************");
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                group.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void nettyNioServer() {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup , workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .localAddress(SERVER_ADDRESS)
                        .childHandler(new ChannelInitializer<io.netty.channel.socket.SocketChannel>() {

                            @Override
                            protected void initChannel(io.netty.channel.socket.SocketChannel ch) throws Exception {
                                System.out.println("[" + Thread.currentThread().getName() + "] Thread , Accepted Connection from + " + ch.remoteAddress());
                                ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                                    @Override
                                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                        String msg = "Hi!";
                                        ctx.writeAndFlush(Unpooled.copiedBuffer(msg , CharsetUtil.UTF_8))
                                                .addListener(ChannelFutureListener.CLOSE);
                                        System.out.println("[" + Thread.currentThread().getName() + "] Thread , write msg -> " + msg);
                                    }
                                });
                            }
                        });
            ChannelFuture future = serverBootstrap.bind().sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                bossGroup.shutdownGracefully().sync();
                workerGroup.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


}
